/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.qudexapps.xmllib.services;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.igfay.jfig.JFigException;

import fedora.fedoraSystemDef.foxml.DatastreamType;
import fedora.fedoraSystemDef.foxml.DatastreamVersionType;
import fedora.fedoraSystemDef.foxml.DigitalObjectDocument;
import fedora.fedoraSystemDef.foxml.StateType;
import fedora.fedoraSystemDef.foxml.DigitalObjectDocument.DigitalObject;

import uk.ljmu.fedoraifz.restrequests.FedoraOperationsAPIM;
import uk.ljmu.fedoraifz.restrequests.FedoraOperationsAPIMImpl;
import uk.ljmu.metadata.schemasutilities.SchemasProcessing;
import uk.ljmu.metadata.utilities.MetadataUtilities;
import uk.ljmu.qudexapps.xmllib.exceptions.FoxmlException;
import uk.ljmu.qudexapps.xmllib.model.FoxmlBeans;
import uk.ljmu.qudexapps.xmllib.utilities.Constants;
import uk.ljmu.qudexapps.xmllib.utilities.IngestionResults;

import uk.da.dext.QudexDocument;
import uk.da.dext.datastructs.CategoryStruct;
import uk.da.dext.datastructs.CodeStruct;
import uk.da.dext.datastructs.MemoStruct;
import uk.da.dext.exceptions.QudexException;
import uk.da.dext.genxml.QudexManagement;

/**
 * Class to manage the creation of Fedora Foxml Objects containing categories,
 * codes, memos and segments
 * 
 * @author agustinamartinez
 * 
 */
public class QudexFedoraObjects {

    private static Logger log = Logger.getLogger(QudexFedoraObjects.class);
    /**
     * Fedora communication Object instance to handle communication
     * requests/responses from the repository
     */
    private FedoraOperationsAPIM fedoraObject;

    /**
     * Holds the full path to the temporary spreadsheet that will be stored
     * within a Collection Object
     */
    private String spreadsheetPath;

    /**
     * Admin Fedora username - Extracted from configuration
     */
    private String fedoraUser;

    private List<IngestionResults> objResults;

    // Categories
    private Map<String, List<String>> categoriesPidsMap;
    // Memos
    private List<String> memosPids;
    // Codes
    private List<String> codesPids;

    /**
     * @return the objResults
     */
    public List<IngestionResults> getObjResults() {
	return objResults;
    }

    /**
     * @param objResults
     *            the objResults to set
     */
    public void setObjResults(List<IngestionResults> objResults) {
	this.objResults = objResults;
    }

    /**
     * @param fedoraObject
     *            the fedoraObject to set
     */
    public void setFedoraObject(FedoraOperationsAPIM fedoraObject) {
	this.fedoraObject = fedoraObject;
    }

    /**
     * @return the fedoraObject
     */
    public FedoraOperationsAPIM getFedoraObject() {
	return fedoraObject;
    }

    /**
     * @param fedoraUser
     *            the fedoraUser to set
     */
    public void setFedoraUser(String fedoraUser) {
	this.fedoraUser = fedoraUser;
    }

    /**
     * @return the fedoraUser
     */
    public String getFedoraUser() {
	return fedoraUser;
    }

    /**
     * @param spreadsheetPath
     *            the spreadsheetPath to set
     */
    public void setSpreadsheetPath(String spreadsheetPath) {
	this.spreadsheetPath = spreadsheetPath;
    }

    /**
     * @return the spreadsheetPath
     */
    public String getSpreadsheetPath() {
	return spreadsheetPath;
    }

    public QudexFedoraObjects() throws JFigException {
	this.fedoraObject = new FedoraOperationsAPIMImpl();
	this.categoriesPidsMap = new HashMap<String, List<String>>();
	this.codesPids = new ArrayList<String>();
	this.memosPids = new ArrayList<String>();
	this.objResults = new ArrayList<IngestionResults>();
    }

    public QudexFedoraObjects(FedoraOperationsAPIM fedObj) {
	this.fedoraObject = fedObj;
	this.categoriesPidsMap = new HashMap<String, List<String>>();
	this.codesPids = new ArrayList<String>();
	this.memosPids = new ArrayList<String>();
	this.objResults = new ArrayList<IngestionResults>();
    }

    public QudexFedoraObjects(String user, String password) {
	this.fedoraObject = new FedoraOperationsAPIMImpl(user, password);
	this.categoriesPidsMap = new HashMap<String, List<String>>();
	this.codesPids = new ArrayList<String>();
	this.memosPids = new ArrayList<String>();
	this.objResults = new ArrayList<IngestionResults>();
    }

    // FEDORA OPERATIONS
    /**
     * Performs Fedora APIM call to ingest a new Foxml object in the repository
     * 
     * @param namespace
     *            PID namespace obtained from collection
     * @param contents
     *            Foxml contents of the object
     * @param foxmlPid
     *            the object's assigned PID
     * @throws JFigException
     */
    public String ingestObjectFedora(String label, String namespace,
	    String contents, String foxmlPid) throws FoxmlException,
	    JFigException {
	StringBuilder sb = new StringBuilder();
	FedoraOperationsAPIM fedObj;

	if (this.fedoraObject != null)
	    fedObj = this.fedoraObject;
	else
	    fedObj = new FedoraOperationsAPIMImpl();

	/*
	 * AMG- Ingest parameters: object PID, Foxml XML contents, mimetype,
	 * object label, log message, Object schema version, object format
	 * storage (UTF-8, ISO), object namespace, object owner, object status
	 * (active, deleted, inactive) and ignore mimetype parameter
	 */
	String resultPid = fedObj.createObject(foxmlPid, (String) contents,
		Constants.DEFAULT_MIMETYPE, label, "Digital Object Creation",
		MetadataUtilities.NSMAP.get("foxml1.1"), Constants.ISO_8859_1,
		namespace, fedoraUser, StateType.A.toString(), "");

	if (resultPid != null && !resultPid.equals("")
		&& resultPid.contains(Constants.PID_DELIMITER)) {

	    return resultPid;
	} else {
	    sb.append("Object not ingested, error in ingestObjectFedora: ");
	    sb.append(resultPid);
	    log.fatal(sb.toString());
	    throw new FoxmlException(sb.toString());
	}
    }

    /**
     * 
     * @param pid
     * @param removeCompletely
     * @return
     */
    public String deleteObjectFedora(String pid, boolean removeCompletely) {
	// At the moment temporarily it just marks the object as deleted without
	// physically removing it
	String res = "";
	if (removeCompletely) {
	    res = this.fedoraObject.deleteObject(pid);
	    // if (res == null) {
	    // this.addFedoraResultsMessage(pid,
	    // "Error while deleting object with PID: " + pid
	    // + ". Contact Administrator.");
	    // }
	} else {
	    res = this.fedoraObject.modifyObject(pid, "", this.fedoraUser,
		    StateType.D.toString(), "Marking object as deleted", "",
		    "", Constants.DEFAULT_MIMETYPE);
	    return res;
	}
	return res;
    }

    /**
     * 
     * @param collectionPid
     * @return
     */
    public boolean isCollectionWithQudexInstance(String collectionPid) {
	String res = this.getFedoraObject().getDatastream(collectionPid,
		Constants.QUDEX_INSTANCE_DS_NAME, "", "xml", false);
	if (!res.startsWith(Constants.XML_UTF_VERSION_HEADER))
	    return false;
	else
	    return true;
    }
    /**
     * 
     * @param collectionPid
     * @return
     */
    public String getCollectionWithQudexInstance(String collectionPid) {
	String res = this.getFedoraObject().getDatastreamContent(collectionPid,
		Constants.QUDEX_INSTANCE_DS_NAME);
	if (res.startsWith(Constants.QUDEX_INSTANCE_DS_CONTENT))
	    return res;
	else
	    return null;
    }

    /**
     * 
     * @param value
     * @return
     */
    public boolean objectExists(String value) {
	String res = this.getFedoraObject().getObjectXML(value);
	if (res != null && res.startsWith(Constants.XML_UTF_VERSION_HEADER))
	    return true;
	else
	    return false;
    }

    /**
     * 
     * @return
     */
    public String updateObjectFedora(FoxmlBeans foxml) {
	StringBuilder sb = new StringBuilder();
	String result = foxml.getFoxmlDoc().getDigitalObject().getPID();
	sb.append(Constants.XML_VERSION_HEADER);
	sb.append(foxml.getFoxmlDoc().toString());

	DatastreamType[] dsArray = foxml.getFoxmlDoc().getDigitalObject()
		.getDatastreamArray();

	for (DatastreamType datastream : dsArray) {
	    DatastreamVersionType dstype = datastream
		    .getDatastreamVersionArray(0);
	    String requestres = this.getFedoraObject().modifyDatastream(
		    foxml.getFoxmlDoc().getDigitalObject().getPID(),
		    datastream.getID(), dstype.getXmlContent().toString(), "",
		    dstype.getLABEL(), true, datastream.getSTATE().toString(),
		    "", "", "", dstype.getMIMETYPE(),
		    "Updating collection datastream", false, "");
	    if (requestres == null) {
		// ERROR
		result = null;
	    }
	}

	return result;

    }

    /**
     * 
     * @return
     */
    public boolean addQudexInstanceToFedoraCollection(FoxmlBeans foxml,
	    List<String> qudexCollectionsPIDs, QudexDocument qudexInstance) {
	for (String parentPid : qudexCollectionsPIDs) {
	    foxml.initFoxmlDocumentObject();
	    String foxmlStr = this.getFedoraObject().getObjectXML(parentPid);
	    if (foxmlStr.contains(Constants.XML_UTF_VERSION_HEADER)) {
		try {
		    foxml.setFoxmlDoc(DigitalObjectDocument.Factory
			    .parse(foxmlStr));
		} catch (XmlException e1) {
		    log.fatal(e1.getMessage());
		    return false;
		}
		try {
		    foxml.addDatastream("QudexInstance",
			    DatastreamType.CONTROLGROUP.X,
			    "Qudex XML Instance", StateType.A, qudexInstance
				    .toString(), Constants.DEFAULT_MIMETYPE,
			    true, false); /*isVersionable FALSE, instance can be quite big for Internal XML DS*/
		    return true;
		} catch (FoxmlException e) {
		    log.fatal(e.getMessage());
		    return false;
		}
	    }
	}
	return false;
    }

    /**
     * Remove all the objects in the specified list
     * 
     * @param mylist
     *            list with PIDs of the objects to remove
     */
    public void deleteAllFedoraDocuments(List<String> mylist) {
	for (String pid : mylist) {
	    String res = this.deleteObjectFedora(pid, true);
	    if (res == null) {
		log.error("Error while deleting object with PID: " + pid);
		this.addFedoraResultsMessage(pid,
			"Error while deleting object with PID: " + pid
				+ ". Contact Administrator.");
	    }
	}
    }

    public boolean deleteQudexCollection(String collectionPid,
	    boolean removeCompletely) {
	try {
	    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
	    // Remove Collection
	    String res = this.deleteObjectFedora(collectionPid,
		    removeCompletely);
	    if (res == null || res.trim().equals(collectionPid)) {
		this.addFedoraResultsMessage(collectionPid,
			"Error while deleting object with PID: "
				+ collectionPid + ". Object not present.");
		return false;
	    }
	    if (removeCompletely) {
		List<String> depFiles = mulgService
			.retrieveDependantQudexDocuments(collectionPid);
		if (depFiles == null) {
		    this.addFedoraResultsMessage(collectionPid,
			    "Error while deleting object with PID: "
				    + collectionPid
				    + ". Contact Administrator.");
		    return false;
		}
		for (String pid : depFiles) {
		    res = this.deleteObjectFedora(pid, removeCompletely);
		    if (res == null) {
			this.addFedoraResultsMessage(pid,
				"Error while deleting dependant Document with PID: "
					+ pid + ". Contact Administrator.");
			return false;
		    }
		}
	    }
	    return true;
	} catch (JFigException e) {
	    log.error("Error in deleteQudexCollection: ", e);
	    return false;
	}
    }

    public boolean deleteQudexCollectionRoot(String collectionPid,
	    boolean removeCompletely, RelationshipsManagement relManager) {
	try {
	    // FIND ROOT COLLECTION
	    if (removeCompletely) {
		collectionPid = relManager.getRootCollection(collectionPid);
		if (collectionPid == null)
		    return false;
	    }
	    FedoraMulgaraService mulgService = new FedoraMulgaraServiceImpl();
	    // Remove Collection
	    String res = this.deleteObjectFedora(collectionPid,
		    removeCompletely);
	    if (res == null || res.trim().equals(collectionPid)) {
		this.addFedoraResultsMessage(collectionPid,
			"Error while deleting object with PID: "
				+ collectionPid + ". Object not present.");
		return false;
	    }
	    if (removeCompletely) {
		List<String> depFiles = mulgService
			.retrieveDependantQudexDocuments(collectionPid);
		if (depFiles == null) {
		    this.addFedoraResultsMessage(collectionPid,
			    "Error while deleting object with PID: "
				    + collectionPid
				    + ". Contact Administrator.");
		    return false;
		}
		for (String pid : depFiles) {
		    res = this.deleteObjectFedora(pid, removeCompletely);
		    if (res == null) {
			this.addFedoraResultsMessage(pid,
				"Error while deleting dependant Document with PID: "
					+ pid + ". Contact Administrator.");
			return false;
		    }
		}
	    }
	    return true;
	} catch (JFigException e) {
	    log.error("Error in deleteQudexCollection: ", e);
	    return false;
	}
    }

    public void addFedoraResultsMessage(String identifier, String message) {
	// RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
	IngestionResults obj = new IngestionResults(identifier, message);
	// ADD INFORMATION TO THE ARRAY OF RESULTS
	this.objResults.add(obj);
    }

    /**
     * Create an empty FOXML XML document with auto-generated PID
     * 
     * @param namespace
     *            [optional] namespace used to generate the object PID
     * @param xmlFormat
     *            true if we want the results of the request in XML format
     * 
     * @return FoxmlBeans empty object with a new PID assigned
     */
    private void createFoxmlFile(FoxmlBeans foxml, String namespace,
	    String xmlFormat) throws FoxmlException {
	String pid = "";
	String pidListXML = "";
	DigitalObject digObj;

	// AMG- Obtain current Foxml Digital Object instance
	digObj = (foxml.getFoxmlDoc().getDigitalObject() != null) ? foxml
		.getFoxmlDoc().getDigitalObject() : foxml.getFoxmlDoc()
		.addNewDigitalObject();
	// Add OWNER
	foxml.addOwnerIDProperty(this.getFedoraUser());

	log.info("Creating new PID for the Object...");

	// AMg- Parameters: number of PIDs, optional namespace associate to the
	// PID, XML output format
	pidListXML = this.getFedoraObject().getNextPID(1, namespace, xmlFormat);

	if (pidListXML == null || pidListXML == "") {
	    // NO PID GENERATED
	    // ERROR Exit
	    log.fatal("NO NEW PID generated, in method _CreateFoxmlFile");
	    throw new FoxmlException(
		    "NO NEW PID generated, in method createFoxmlFile");
	}

	// AMG- Parse XML response to obtain the generated PID
	try {
	    Document doc = DocumentHelper.parseText(pidListXML);
	    Element pidElem = doc.getRootElement().element("pid");
	    pid = pidElem.getText();
	    digObj.setPID(pid.trim());
	    log.info("New PID generated: " + pid.trim());
	} catch (DocumentException ex) {
	    log.fatal("In createFOXML: " + ex.getMessage());
	    throw new FoxmlException(
		    "PID not assigned, in method createFoxmlFile");
	}
    }

    // CODES OPERATIONS

    /**
     * @param code
     * @param qudexManager
     * @return boolean
     */
    public boolean generateCodes(CodeStruct code, QudexManagement qudexManager, String rootCollectionID) {
	boolean results;
	StringBuilder sb = new StringBuilder();

	results = createCodeFedoraObject(code, qudexManager, rootCollectionID);
	if (!results) {
	    sb.append("Errors creating Foxml for code ");
	    sb.append(code.getCodeDisplayLabel());
	    this.addFedoraResultsMessage("QudexFedoraObjects", sb.toString());
	    sb.delete(0, sb.toString().length());

	    return false;
	}

	// Create category relationships
	results = this.createCodesRelationships(code);
	if (!results) {
	    sb.append("Errors creating relationships for code ");
	    sb.append(code.getCodeDisplayLabel());
	    this.addFedoraResultsMessage("QudexFedoraObjects", sb.toString());
	    sb.delete(0, sb.toString().length());
	    return false;
	}

	return true;
    }

    /**
     * 
     * @param code
     * @return
     */
    private boolean createCodesRelationships(CodeStruct code) {
	String relURI = MetadataUtilities.MDSchemasUrl.get("rel");
	// AMG Call Ingest Datastream Method
	// Document category relationships
	for (String documentPid : code.getDocumentRefs()) {
	    // Added bi-directional relationship
	    String results = this.getFedoraObject()
		    .addRelationship(documentPid,
			    MetadataUtilities.OBJECTID + documentPid,
			    relURI + "hasAnnotation",
			    MetadataUtilities.OBJECTID + code.getCodePid(),
			    "false", "");
	    if (results == null)
		return false;
	}
	return true;
    }

    /**
     * 
     * @param code
     * @param qudexManager
     * @return
     */
    public boolean createCodeFedoraObject(CodeStruct code,
	    QudexManagement qudexManager, String rootCollectionID) {
	StringBuilder sb = new StringBuilder();
	// List of metadata namespaces prefixes
	List<String> list = new ArrayList<String>();
	boolean resultsOK = false;
	FoxmlBeans foxml = new FoxmlBeans(this.fedoraObject);
	// AMG - (1) Create empty FOXML Category Object
	foxml.initFoxmlDocumentObject();
	try {
	    this.createFoxmlFile(foxml, Constants.DEF_QUDEX_NS, "true");
	    foxml.addLabelProperty("Code Object for the study");
	} catch (FoxmlException ex) {
	    sb.append("Error while creating Foxml Code instance: ");
	    sb.append(ex.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}
	// AMG - (1) Add Datastreams to Document Object
	// AMG - (2) Manage RELS-EXT, add 'isMemberOf' to the appropriate
	// collection
	Document doc;
	list.add("rel");
	doc = SchemasProcessing.contructRDFDocument(foxml.getFoxmlDoc()
		.getDigitalObject().getPID(), list);
	//ADD isPartOf relationship to the root collection
	resultsOK = SchemasProcessing.addRDFProperty("rel",
			"isPartOf", rootCollectionID.contains(MetadataUtilities.OBJECTID) ? rootCollectionID : MetadataUtilities.OBJECTID + rootCollectionID,
			uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_RES, doc); 
	for (String col : code.getDocumentRefs()) {
	    resultsOK = SchemasProcessing.addRDFProperty("rel",
		    "isAnnotationOf", MetadataUtilities.OBJECTID + col,
		    uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_RES, doc);
	}
	// AMG- For Qudex, non DC elements go into RELS-EXT DS, with subject the
	// OBJECT PID
	try {
	    foxml.addDatastream(Constants.DEFAULT_RELSEXT_DS_NAME,
		    DatastreamType.CONTROLGROUP.X,
		    "RELS-EXT record for the object", StateType.A, doc.asXML(),
		    Constants.DEFAULT_MIMETYPE, false, false); /*isVersionable FALSE*/
	} catch (FoxmlException e) {
	    sb.append("Error while adding RELS-EXT DS to categories FOXML: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}

	// AMG- (3) CALL TO INGEST METHOD
	sb.append(Constants.XML_VERSION_HEADER);
	sb.append(foxml.getFoxmlDoc().toString());
	try {
	    String results = ingestObjectFedora("QuDEx Code Object",
		    Constants.DEF_QUDEX_NS, sb.toString(), foxml.getFoxmlDoc()
			    .getDigitalObject().getPID());
	    if (!results.startsWith(Constants.DEF_QUDEX_NS)) {
		// AMG - Object not generated
		sb
			.append("Error while creating Code Object: object not ingested");
		log.fatal(sb.toString());
		return false;

	    }
	} catch (FoxmlException e) {
	    sb.append("Error while creating Code Object: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	} catch (JFigException e) {
	    sb.append("Error while creating Code Object: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}
	// AMG - (4) Manage datastreams creation
	resultsOK = generateCodeDatastream(code, foxml, qudexManager);
	if (!resultsOK) {
	    sb
		    .append("Error while adding Code Xml Snippet to Code Fedora object.");
	    log.fatal(sb.toString());
	    return false;
	}
	code.setCodePid(foxml.getFoxmlDoc().getDigitalObject().getPID());

	return true;
    }

    /**
     * 
     * @param code
     * @param foxml
     * @param qudexManager
     * @return
     */
    private boolean generateCodeDatastream(CodeStruct code, FoxmlBeans foxml,
	    QudexManagement qudexManager) {
	StringBuilder sb = new StringBuilder();
	boolean resultsOk = true;
	// AMG - (1) Create Qudex XML snippet for the category
	try {
	    String qudexCodeSnippet = QudexManagement.generateCodeSnippet(code
		    .getCodeXmlId(), code.getCodeLabel(), code
		    .getCodeDisplayLabel(), code.getCodeAuthority(), code
		    .getCodeLanguage());
	    if (qudexCodeSnippet != null) {
		// AMG - (2) create category datastream
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(qudexCodeSnippet);
		foxml.addDatastream(this.createCodeDSName(code.getCodeLabel()),
			DatastreamType.CONTROLGROUP.X, "Code datastream",
			StateType.A, sb.toString(), Constants.DEFAULT_MIMETYPE,
			true, true); /*isVersionable TRUE small DS*/
		// AMG - Add Code Snippet if the DS has been created
		resultsOk = qudexManager.addCode(sb.toString());
		if (!resultsOk)
		    return false;
		sb.delete(0, sb.toString().length());
	    } else {
		log
			.fatal("generateCodeDatastreams, error generating code xml snippet for code: "
				+ code.getCodeDisplayLabel());
		return false;
	    }
	} catch (QudexException e) {
	    log.fatal(e.getMessage());
	} catch (FoxmlException e) {
	    log.fatal("Error creating code datastream: " + e.getMessage());
	    return false;
	}

	return true;
    }

    // CATEGORIES OPERATIONS
    /**
     * @param cat
     * @param qudexManager
     * @return boolean
     */
    public boolean generateCategories(CategoryStruct cat,
	    QudexManagement qudexManager, String rootCollectionID) {
	boolean results;
		StringBuilder sb = new StringBuilder();

		if (cat.isExistingCategory()) {
			results = updateFedoraCategoryObject(cat, qudexManager,
					rootCollectionID);
			if (!results) {
				sb.append("Errors creating category Foxml for category ");
				sb.append(cat.getCategoryDisplayLabel());
				this.addFedoraResultsMessage("QudexFedoraObjects", sb.toString());
				sb.delete(0, sb.toString().length());

				return false;
			}
			List<String> mlist = new ArrayList<String>();
			mlist.add("true");
			this.categoriesPidsMap.put(cat.getCategoryPid(), mlist);

		} else {
		results = createCategoryFedoraObject(cat, qudexManager,
				rootCollectionID);
		if (!results) {
			sb.append("Errors creating category Foxml for category ");
			sb.append(cat.getCategoryDisplayLabel());
			this.addFedoraResultsMessage("QudexFedoraObjects", sb.toString());
			sb.delete(0, sb.toString().length());

			return false;
		}
		// Parents
		List<String> mlist = new ArrayList<String>();
		if (cat.getImmParents().size() > 0 && cat.getImmParents().get(0) != "") {
			for (String parent : cat.getImmParents()) {
				CategoryStruct parentCat = (CategoryStruct) qudexManager
						.getCategoryManager().findElementByLabel(parent);
				// boolean resultsOK = createCategoryFedoraObject(parentCat,
				// qudexManager, rootCollectionID);
				// if (!resultsOK) {
				// sb.append("Errors creating category Foxml for category ");
				// sb.append(parentCat.getCategoryDisplayLabel());
				// this.addFedoraResultsMessage("QudexFedoraObjects", sb
				// .toString());
				// sb.delete(0, sb.toString().length());

				// return false;
				// }
				mlist.add(parentCat.getCategoryPid());
			}
			this.categoriesPidsMap.put(cat.getCategoryPid(), mlist);
		}		

		}
		
	// Create category relationships
	// results = this.createCategoriesRelationships(cat);
	results = this.addCategoryRelationshipsToRelsext(cat);
	if (!results) {
	    sb.append("Errors creating category relationships for category ");
	    sb.append(cat.getCategoryDisplayLabel());
	    this.addFedoraResultsMessage("QudexFedoraObjects", sb.toString());
	    sb.delete(0, sb.toString().length());
	    return false;
	}

	return true;
    }

    /**
     * Creates the Categories FOXML object associated with a Qudex Qualitative
     * Collection
     * 
     * @param fedoraObj
     *            Fedora communication object
     * @param qudexManager
     *            Qudex XML management object
     * 
     */
    public boolean createCategoryFedoraObject(CategoryStruct category,
	    QudexManagement qudexManager, String rootCollectionID) {
	StringBuilder sb = new StringBuilder();
	// List of metadata namespaces prefixes
	List<String> list = new ArrayList<String>();
	boolean resultsOK = false;
	FoxmlBeans foxml = new FoxmlBeans(this.fedoraObject);
	// AMG - (1) Create empty FOXML Category Object
	foxml.initFoxmlDocumentObject();
	try {
	    this.createFoxmlFile(foxml, Constants.DEF_QUDEX_NS, "true");
	    foxml.addLabelProperty("Categories Object for the study");
	} catch (FoxmlException ex) {
	    sb.append("Error while creating Foxml Category instance: ");
	    sb.append(ex.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}
	// AMG - (1) Add Datastreams to Document Object
	// AMG - (2) Manage RELS-EXT, add 'isMemberOf' to the appropriate
	// collection
	Document doc;
	list.add("rel");
	doc = SchemasProcessing.contructRDFDocument(foxml.getFoxmlDoc()
		.getDigitalObject().getPID(), list);
	//ADD isPartOf relationship to the root collection
	resultsOK = SchemasProcessing.addRDFProperty("rel",
		    "isPartOf", rootCollectionID.contains(MetadataUtilities.OBJECTID) ? rootCollectionID : MetadataUtilities.OBJECTID + rootCollectionID,
		    uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_RES, doc);
	
	//AMG Added Category label using RDFS Label
	resultsOK = SchemasProcessing.addRDFProperty("rdfs",
		    "label", category.getCategoryDisplayLabel(),
		    uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_PROP, doc);
	for (String col : category.getDocumentRefs()) {
	    resultsOK = SchemasProcessing.addRDFProperty("rel",
		    "isAnnotationOf", MetadataUtilities.OBJECTID + col,
		    uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_RES, doc);
	}
	// AMG- For Qudex, non DC elements go into RELS-EXT DS, with subject the
	// OBJECT PID
	try {
	    foxml.addDatastream(Constants.DEFAULT_RELSEXT_DS_NAME,
		    DatastreamType.CONTROLGROUP.X,
		    "RELS-EXT record for the object", StateType.A, doc.asXML(),
		    Constants.DEFAULT_MIMETYPE, false, false); /* Versionable FALSE, rels-ext DS */
	} catch (FoxmlException e) {
	    sb.append("Error while adding RELS-EXT DS to categories FOXML: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}

	// AMG- (3) CALL TO INGEST METHOD
	sb.append(Constants.XML_VERSION_HEADER);
	sb.append(foxml.getFoxmlDoc().toString());
	try {
	    String results = ingestObjectFedora("QuDEx Category Object",
		    Constants.DEF_QUDEX_NS, sb.toString(), foxml.getFoxmlDoc()
			    .getDigitalObject().getPID());
	    if (!results.startsWith(Constants.DEF_QUDEX_NS)) {
		// AMG - Object not generated
		sb
			.append("Error while creating Category Object: object not ingested");
		log.fatal(sb.toString());
		return false;

	    }
	} catch (FoxmlException e) {
	    sb.append("Error while creating Category Object: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	} catch (JFigException e) {
	    sb.append("Error while creating Category Object: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}
	// AMG Added once the object has been created in the repository, instead of after adding datastream
	category.setCategoryPid(foxml.getFoxmlDoc().getDigitalObject().getPID());
	this.categoriesPidsMap.put(category.getCategoryPid(),
			new ArrayList<String>());
	// AMG - (4) Manage datastreams creation
	resultsOK = generateCategoryDatastream(category, foxml, qudexManager);
	if (!resultsOK) {
	    sb
		    .append("Error while adding Category datastream to Category Fedora object.");
	    log.fatal(sb.toString());
	    return false;
	}
	//category
	//.setCategoryPid(foxml.getFoxmlDoc().getDigitalObject().getPID());
	//this.categoriesPidsMap.put(category.getCategoryPid(),
	//new ArrayList<String>());
	return true;
    }

    /**
     * Method to update an existing category object
     * 
     * @param category
     * @param qudexManager
     * @param rootCollectionID
     * @return true if update performed successfully
     */
    public boolean updateFedoraCategoryObject(CategoryStruct category,
    	    QudexManagement qudexManager, String rootCollectionID) {
    	StringBuilder sb = new StringBuilder();
    	
    	boolean resultsOK = false;
    	FoxmlBeans foxml = new FoxmlBeans(this.fedoraObject);
    	// Get category FOXML
    	String foxmlStr = foxml.getFedoraObject().getObjectXML(category.getCategoryPid());
    	if (foxmlStr != "") {
    		DigitalObjectDocument foxmlDoc;
			try {
				foxmlDoc = DigitalObjectDocument.Factory.parse(foxmlStr);
				foxml.setFoxmlDoc(foxmlDoc);
			} catch (XmlException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	} else {
    		log.fatal("Obtaining fedora category foxml. Error.");
    		return false;
    	}
    	// AMG - (1) Add Datastreams to Document Object
    	// AMG - (2) Manage RELS-EXT, add 'isMemberOf' to the appropriate
    	// collection
    	Document doc;
    	String xmlstr = this.getFedoraObject().getDatastreamContent(category.getCategoryPid(), Constants.DEFAULT_RELSEXT_DS_NAME);
    	try {
			doc = DocumentHelper.parseText(xmlstr);
			for (String col : category.getDocumentRefs()) {
	    	    resultsOK = SchemasProcessing.addRDFProperty("rel",
	    		    "isAnnotationOf", MetadataUtilities.OBJECTID + col,
	    		    uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_RES, doc);
	    	}
			// AMG Modify RELS-EXT DS
	    	xmlstr = this.getFedoraObject().modifyDatastream(category.getCategoryPid(), Constants.DEFAULT_RELSEXT_DS_NAME, doc.asXML(), "", "", null, "", "", "", "", Constants.DEFAULT_MIMETYPE, "RELS-EXT Updated", null, "");
	    	
	    	// AMG - (4) Manage datastreams creation
	    	resultsOK = updateCategoryDatastream(category, foxml, qudexManager);
	    	if (!resultsOK) {
	    	    sb
	    		    .append("Error while adding Category datastream to Category Fedora object.");
	    	    log.fatal(sb.toString());
	    	    return false;
	    	}
		} catch (DocumentException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
    	
    	//category
    	//.setCategoryPid(foxml.getFoxmlDoc().getDigitalObject().getPID());
    	//this.categoriesPidsMap.put(category.getCategoryPid(),
    	//new ArrayList<String>());
    	return true;
    }
    /**
     * 
     * @param category
     * @param foxml
     * @param qudexManager
     * @return
     */
    private boolean generateCategoryDatastream(CategoryStruct category,
	    FoxmlBeans foxml, QudexManagement qudexManager) {
	StringBuilder sb = new StringBuilder();
	boolean resultsOk = true;
	// AMG - (1) Create Qudex XML snippet for the category
	try {
	    String qudexCatSnippet = QudexManagement.generateCategorySnippet(
		    category.getCategoryXmlId(), category
			    .getDocumentRefsXmlIds(),
		    category.getImmChildren(), category.getCategoryLabel(),
		    category.getCategoryDisplayLabel(), category
			    .getCategoryLanguage());
	    if (qudexCatSnippet != null) {
		// AMG - (2) create category datastream
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(qudexCatSnippet);
		foxml.addDatastream(this.createCategoryDSName(category
			.getCategoryLabel()), DatastreamType.CONTROLGROUP.X,
			"Category datastream", StateType.A, sb.toString(),
			Constants.DEFAULT_MIMETYPE, true, true); /*versionable TRUE, small DS*/
		// AMG - Add Category Snippet if the DS has been created
		resultsOk = qudexManager.addCategory(sb.toString());
		if (!resultsOk)
		    return false;
		sb.delete(0, sb.toString().length());
	    } else {
		log
			.fatal("generateCategoryDatastreams, error generating category xml snippet for category: "
				+ category.getCategoryDisplayLabel());
		return false;
	    }
	} catch (QudexException e) {
	    log.fatal(e.getMessage());
	} catch (FoxmlException e) {
	    log.fatal("Error creating category datastream: " + e.getMessage());
	    return false;
	}

	return true;
    }
    
    /**
     * 
     * @param category
     * @param foxml
     * @param qudexManager
     * @return
     */
    private boolean updateCategoryDatastream(CategoryStruct category,
	    FoxmlBeans foxml, QudexManagement qudexManager) {
	StringBuilder sb = new StringBuilder();
	boolean resultsOk = true;
	// AMG - (1) Create Qudex XML snippet for the category
	try {
	    String qudexCatSnippet = QudexManagement.generateCategorySnippet(
		    category.getCategoryXmlId(), category
			    .getDocumentRefsXmlIds(),
		    category.getImmChildren(), category.getCategoryLabel(),
		    category.getCategoryDisplayLabel(), category
			    .getCategoryLanguage());
	    if (qudexCatSnippet != null) {
		// AMG - (2) create category datastream
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(qudexCatSnippet);
		this.getFedoraObject().modifyDatastream(category.getCategoryPid(), 
		this.createCategoryDSName(category
				.getCategoryLabel()), sb.toString(), "", "", null, "", "", "", "", Constants.DEFAULT_MIMETYPE, "Category snippet updated", null, "");
		
		// AMG - Update QuDEx category
		resultsOk = qudexManager.updateCategory(sb.toString());
		if (!resultsOk)
		    return false;
		sb.delete(0, sb.toString().length());
	    } else {
		log
			.fatal("generateCategoryDatastreams, error generating category xml snippet for category: "
				+ category.getCategoryDisplayLabel());
		return false;
	    }
	} catch (QudexException e) {
	    log.fatal(e.getMessage());
	}

	return true;
    }

    /**
     * Parses the categories map to add all the relationships between categories
     * for a given qudex study. All the categories datastreams are already
     * present in the categories object.
     * 
     * @return true if the datastream is created succesfully, false otherwise
     */
    @SuppressWarnings("unused")
    private boolean createCategoriesRelationships(CategoryStruct category, String rootCollectionID) {
	;
	String relURI = MetadataUtilities.MDSchemasUrl.get("rel");
	
	//ADD isPartOf relationship to the root collection
	String results = this.getFedoraObject().addRelationship(category.getCategoryPid(), 
			MetadataUtilities.OBJECTID + category.getCategoryPid(), 
			relURI + "isPartOf", 
			rootCollectionID.contains(MetadataUtilities.OBJECTID) ? rootCollectionID : MetadataUtilities.OBJECTID + rootCollectionID, 
				"false", "");
	if (results == null)
		return false;
	List<String> mList = this.categoriesPidsMap.get(category
		.getCategoryPid());
	for (String parent : mList) {
	    // Test API for relationships
	    results = this.getFedoraObject().addRelationship(
		    category.getCategoryPid(),
		    MetadataUtilities.OBJECTID + category.getCategoryPid(),
		    relURI + "isMemberOf", MetadataUtilities.OBJECTID + parent,
		    "false", "");
	    if (results == null)
		return false;
	    // Added bi-directional relationship
	    results = this.getFedoraObject().addRelationship(parent,
		    MetadataUtilities.OBJECTID + parent, relURI + "hasMember",
		    MetadataUtilities.OBJECTID + category.getCategoryPid(),
		    "false", "");
	    if (results == null)
		return false;
	}
	// AMG Call Ingest Datastream Method
	// Document category relationships
	for (String documentPid : category.getDocumentRefs()) {
	    // Added bi-directional relationship
	    results = this.getFedoraObject().addRelationship(
		    documentPid, MetadataUtilities.OBJECTID + documentPid,
		    relURI + "hasAnnotation",
		    MetadataUtilities.OBJECTID + category.getCategoryPid(),
		    "false", "");
	    if (results == null)
		return false;
	}
	return true;
    }

    // NEW METHOD THAT GENERATES AT ONCE THE RELS-EXT DATASTREAM WITH THE
    // RELATIONSHIPS FOR A GIVEN CATEGORY
    private boolean addCategoryRelationshipsToRelsext(CategoryStruct category) {
	String relURI = MetadataUtilities.MDSchemasUrl.get("rel");
	List<String> mList = this.categoriesPidsMap.get(category
		.getCategoryPid());
	// Get Datastream data
	String dsData = this.getFedoraObject().getDatastreamContent(
		category.getCategoryPid(), Constants.DEFAULT_RELSEXT_DS_NAME);
	if (dsData != null) {
	    try {
		Document doc = DocumentHelper.parseText(dsData);
		Element rootElement = doc.getRootElement();
		Element description = rootElement.element("Description");

		for (String parent : mList) {
		    // Test API for relationships
		    Element relationship = description
			    .addElement("rel:isMemberOf");
		    relationship.addAttribute("rdf:resource",
			    MetadataUtilities.OBJECTID + parent);
		    // String results = this.getFedoraObject().addRelationship(
		    // category.getCategoryPid(),
		    // MetadataUtilities.OBJECTID
		    // + category.getCategoryPid(),
		    // relURI + "isMemberOf",
		    // MetadataUtilities.OBJECTID + parent, "false", "");
		    // if (results == null)
		    // return false;
		    // Added bi-directional relationship
		    String results = this.getFedoraObject().addRelationship(
			    parent,
			    MetadataUtilities.OBJECTID + parent,
			    relURI + "hasMember",
			    MetadataUtilities.OBJECTID
				    + category.getCategoryPid(), "false", "");
		    if (results == null)
			return false;
		}
		// Add modified datastream
		String results = this.fedoraObject.modifyDatastream(category
			.getCategoryPid(), Constants.DEFAULT_RELSEXT_DS_NAME,
			doc.asXML(),
			"",
			Constants.DEFAULT_RELSEXT_DS_NAME,
			false, // DS Non versionable
			StateType.A.toString(), "", "", "",
			MetadataUtilities.MIMETYPES_MAP.get("rdf"),
			"updating category relationships", false, "");
		if (results == null) {
		    return false;
		}
	    } catch (DocumentException e) {
		return false;
	    }
	} else
	    return false;
	// AMG Call Ingest Datastream Method
	// Document category relationships
	for (String documentPid : category.getDocumentRefs()) {
	    // Added bi-directional relationship
	    String results = this.getFedoraObject().addRelationship(
		    documentPid, MetadataUtilities.OBJECTID + documentPid,
		    relURI + "hasAnnotation",
		    MetadataUtilities.OBJECTID + category.getCategoryPid(),
		    "false", "");
	    if (results == null)
		return false;
	}
	return true;
    }

    // MEMOS OPERATIONS
    /**
     * @param qudexManager
     * @return boolean
     */
    public boolean generateMemos(MemoStruct memo, QudexManagement qudexManager, String rootCollectionID) {

	boolean results;
	StringBuilder sb = new StringBuilder();

	results = createMemoFedoraObject(memo, qudexManager, rootCollectionID);
	if (!results) {
	    sb.append("Errors creating Foxml for Memo Element ");
	    sb.append(memo.getMemoLabel());
	    this.addFedoraResultsMessage("QudexFedoraObjects", sb.toString());
	    sb.delete(0, sb.toString().length());

	    return false;
	}
	results = this.createMemosRelationships(memo);
	if (!results) {
	    sb.append("Errors creating memo relationships for memo ");
	    sb.append(memo.getMemoLabel());
	    this.addFedoraResultsMessage("QudexFedoraObjects", sb.toString());
	    sb.delete(0, sb.toString().length());
	}

	return true;
    }

    /**
     * 
     * @param memo
     * @param qudexManager
     * @return
     */
    private boolean createMemoFedoraObject(MemoStruct memo,
	    QudexManagement qudexManager, String rootCollectionID) {
	StringBuilder sb = new StringBuilder();
	// List of metadata namespaces prefixes
	List<String> list = new ArrayList<String>();
	boolean resultsOK = false;
	FoxmlBeans foxml = new FoxmlBeans(this.fedoraObject);
	// AMG - (1) Create empty FOXML Memo Object
	foxml.initFoxmlDocumentObject();
	try {
	    this.createFoxmlFile(foxml, Constants.DEF_QUDEX_NS, "true");
	    foxml.addLabelProperty("Memo Object for the study");
	} catch (FoxmlException ex) {
	    sb.append("Error while creating Foxml Memo instance: ");
	    sb.append(ex.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}
	// AMG - (1) Add Datastreams to Document Object
	// AMG - (2) Manage RELS-EXT, add 'isAnnotationOf' to the appropriate
	// collection
	Document doc;
	list.add("rel");
	doc = SchemasProcessing.contructRDFDocument(foxml.getFoxmlDoc()
		.getDigitalObject().getPID(), list);
	//ADD isPartOf relationship to the root collection
	resultsOK = SchemasProcessing.addRDFProperty("rel",
				"isPartOf", rootCollectionID.contains(MetadataUtilities.OBJECTID) ? rootCollectionID : MetadataUtilities.OBJECTID + rootCollectionID,
				uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_RES, doc); 
	for (String col : memo.getDocumentRefs()) {
	    resultsOK = SchemasProcessing.addRDFProperty("rel",
		    "isAnnotationOf", MetadataUtilities.OBJECTID + col,
		    uk.ljmu.metadata.utilities.Constants.RDFS_TYPE_RES, doc);
	}
	// AMG- For Qudex, non DC elements go into RELS-EXT DS, with subject the
	// OBJECT PID
	try {
	    foxml.addDatastream(Constants.DEFAULT_RELSEXT_DS_NAME,
		    DatastreamType.CONTROLGROUP.X,
		    "RELS-EXT record for the object", StateType.A, doc.asXML(),
		    Constants.DEFAULT_MIMETYPE, false, false); /*Non versionable*/
	} catch (FoxmlException e) {
	    sb.append("Error while adding RELS-EXT DS to categories FOXML: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}

	// AMG- (3) CALL TO INGEST METHOD
	sb.append(Constants.XML_VERSION_HEADER);
	sb.append(foxml.getFoxmlDoc().toString());
	try {
	    String results = ingestObjectFedora("QuDEx Memo Object",
		    Constants.DEF_QUDEX_NS, sb.toString(), foxml.getFoxmlDoc()
			    .getDigitalObject().getPID());
	    if (!results.startsWith(Constants.DEF_QUDEX_NS)) {
		// AMG - Object not generated
		sb
			.append("Error while creating Memo Object: object not ingested");
		log.fatal(sb.toString());
		return false;

	    }
	} catch (FoxmlException e) {
	    sb.append("Error while creating Memo Object: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	} catch (JFigException e) {
	    sb.append("Error while creating Memo Object: ");
	    sb.append(e.getMessage());
	    log.fatal(sb.toString());
	    return false;
	}
	// AMG - (4) Manage datastreams creation
	resultsOK = generateMemoDatastream(memo, foxml, qudexManager);
	if (!resultsOK) {
	    sb
		    .append("Error while adding Memo datastream to Memo Fedora object.");
	    log.fatal(sb.toString());
	    return false;
	}
	memo.setMemoPid(foxml.getFoxmlDoc().getDigitalObject().getPID());
	return true;
    }

    /**
     * 
     * @param memo
     * @param foxmlBeans
     * @param qudexManager
     * @return
     */
    private boolean generateMemoDatastream(MemoStruct memo,
	    FoxmlBeans foxmlBeans, QudexManagement qudexManager) {
	StringBuilder sb = new StringBuilder();
	boolean resultsOk = true;
	// AMG - (1) Create Qudex XML snippet for each code
	try {
	    String qudexMemoSnippet = QudexManagement.generateMemoSnippet(memo
		    .getMemoXmlId(), memo.getMemoLabel(), memo.getMemoText(),
		    memo.getMemoLanguage(), memo.getDocumentRefsXmlIds());
	    if (qudexMemoSnippet != null) {
		// AMG - (2) create memo datastream
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(qudexMemoSnippet);
		foxmlBeans.addDatastream(this.createMemoDSName(memo
			.getMemoLabel()), DatastreamType.CONTROLGROUP.X,
			"Memo datastream", StateType.A, sb.toString(),
			Constants.DEFAULT_MIMETYPE, true, false); /*Non versionable*/
		// AMG - Add Memo Snippet if the datastream has been
		// created
		resultsOk = qudexManager.addMemo(sb.toString());
		if (!resultsOk)
		    return false;
		sb.delete(0, sb.toString().length());
	    } else {
		log
			.fatal("addMemo, error generating memo xml snippet for memo: "
				+ memo.getMemoLabel());
		return false;
	    }
	} catch (QudexException e) {
	    log.fatal(e.getMessage());
	    return false;
	} catch (FoxmlException e) {
	    log.fatal("Error creating memo datastream: " + e.getMessage());
	    return false;
	}
	return true;
    }

    /**
     * Parses the categories map to add all the relationships between categories
     * for a given qudex study. All the categories datastreams are already
     * present in the categories object.
     * 
     * @return true if the datastream is created succesfully, false otherwise
     */
    private boolean createMemosRelationships(MemoStruct memo) {
	;
	String relURI = MetadataUtilities.MDSchemasUrl.get("rel");
	// AMG Call Ingest Datastream Method
	// Document memo relationships
	for (String documentPid : memo.getDocumentRefs()) {
	    // Added bi-directional relationship
	    String results = this.getFedoraObject()
		    .addRelationship(documentPid,
			    MetadataUtilities.OBJECTID + documentPid,
			    relURI + "hasAnnotation",
			    MetadataUtilities.OBJECTID + memo.getMemoPid(),
			    "false", "");
	    if (results == null)
		return false;
	}
	return true;
    }

    // SEGMENTS OPERATIONS
    public boolean createSegmentFedoraObject(QudexManagement qudexManager) {
	return false;
    }

    // COMMON METHODS
    private String createCategoryDSName(String catLabel) {
	// return Constants.CAT_LABEL_PREFIX + catLabel.replace(" ", "_");
	return Constants.CAT_DS_NAME;
    }

    private String createMemoDSName(String memoLabel) {
	// return Constants.MEMO_LABEL_PREFIX + memoLabel.replace(" ", "_");
	return Constants.MEMO_DS_NAME;
    }

    private String createCodeDSName(String codeLabel) {
	// return Constants.CODE_LABEL_PREFIX + codeLabel.replace(" ", "_");
	return Constants.CODE_DS_NAME;
    }

    /**
     * 
     * @param pid
     * @param displayLabel
     * @return
     */
    @SuppressWarnings("unused")
    private String generateDatastreamPid(String pid, String displayLabel) {
	if (displayLabel.contains(" ")) {
	    displayLabel = displayLabel.replace(" ", "_");
	}
	return MetadataUtilities.OBJECTID + pid + "/" + displayLabel;
    }

    public void cleanCategoriesMap() {
	this.categoriesPidsMap.clear();
    }

    /**
     * If errors present, delete all Foxml for Categories
     */
    public void deleteAllCategories() {
    	// AMG Fixes previous cast exception
    	List<String> keylist = new ArrayList<String>();
    	Set<String> keys = this.categoriesPidsMap.keySet();
    	for (String key : keys) {
    		if (!isExistingCategory(this.categoriesPidsMap.get(key).get(0)))
    			keylist.add(key);
    	}
	this.deleteAllFedoraDocuments(keylist);
    }

    private boolean isExistingCategory(String value) {
    	try {
    	if (value.equalsIgnoreCase("true"))
		return true;
    	else return false;
    	} catch (Exception ex) {
    		log.error(ex.getMessage());
    		return false;
    	}
	}

	public void deleteAllMemos() {
	this.deleteAllFedoraDocuments((List<String>) this.memosPids);
    }

    public void deleteAllCodes() {
	this.deleteAllFedoraDocuments((List<String>) this.codesPids);
    }

    public void cleanQudexFedoraObjects() {
	this.categoriesPidsMap.clear();
	this.codesPids.clear();
	this.memosPids.clear();
    }

    // ADD SPREADSHEETS TO COLLECTION OBJECT
    public boolean addSpreadsheetToObject(String dsName, String objectPid, String mimetype) {
	FoxmlBeans foxml = new FoxmlBeans(this.fedoraObject);
	String foxmlStr = this.getFedoraObject().getObjectXML(objectPid);
	if (foxmlStr.contains(Constants.XML_UTF_VERSION_HEADER)) {
	    try {
		foxml
			.setFoxmlDoc(DigitalObjectDocument.Factory
				.parse(foxmlStr));
	    } catch (XmlException e1) {
		log.fatal(e1.getMessage());
		return false;
	    }
	    try {
		foxml.addDatastream(dsName,
			DatastreamType.CONTROLGROUP.M, "Uploaded spreadsheet",
			StateType.A, this.getSpreadsheetPath() + mimetype,
			mimetype, true, false); /* Not versionable, new version replaces the previous */
		return true;
	    } catch (FoxmlException e) {
		log.fatal(e.getMessage());
		return false;
	    }
	}
	return true;
    }

	public String updateFullObject(FoxmlBeans foxmlBeans) {
		
		StringBuilder sb = new StringBuilder();
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(foxmlBeans.getFoxmlDoc().toString());
		String results = this.fedoraObject.modifyObject(foxmlBeans.getFoxmlDoc().getDigitalObject().getPID(), 
				foxmlBeans.getFoxmlLabel(), 
				"", 
				"", 
				"Update Object", 
				"", 
				sb.toString(), 
				Constants.DEFAULT_MIMETYPE);
		return results;
	}

}
